package com.cauli.search.service.imp;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.ElasticsearchException;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.HighlighterEncoder;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.indices.CreateIndexResponse;
import com.cauli.search.feignService.FileFeignService;
import com.cauli.search.model.dto.FileSearchDTO;
import com.cauli.search.model.dto.SearchFileQueryDTO;
import com.cauli.search.model.vo.SearchFileVO;
import com.cauli.search.service.ElasticsearchService;
import com.cauli.utils.util.ResultResponse;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author Cauli
 * @date 2023/9/1 20:58
 * @description Elasticsearch 服务实现类
 */
@Slf4j
@Service
public class ElasticsearchServiceImpl implements ElasticsearchService {
    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Resource
    private FileFeignService feignService;

    private final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            12, // 核心线程数
            20, // 最大线程数
            1, // 线程存活时间
            TimeUnit.SECONDS, // 存活时间单位
            new ArrayBlockingQueue<>(1000) // 任务队列
    );


    public boolean createIndex(String indexName) {
        CreateIndexResponse createIndexResponse;
        try {
            // 创建索引
            createIndexResponse = elasticsearchClient.indices()
                    .create(c -> c.index(indexName));
            // 打印结果
            log.info("创建索引createIndexResponse.acknowledged() ： " + createIndexResponse.acknowledged());
        } catch (IOException | ElasticsearchException e) {
            throw new RuntimeException(e);
        }
        return Boolean.TRUE.equals(createIndexResponse.acknowledged());
    }

    public void uploadES(FileSearchDTO fileSearchDTO) {
        threadPoolExecutor.execute(() -> {
            try {
                elasticsearchClient.index(i -> i.index("file_search")
                        .id(fileSearchDTO.getUserFileId())
                        .document(fileSearchDTO));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
    }

    public void deleteES(Long userFileId) {
        threadPoolExecutor.execute(() -> {
            try {
                elasticsearchClient.delete(d -> d
                        .index("file_search")
                        .id(String.valueOf(userFileId)));
            } catch (Exception e) {
                log.debug("ES删除操作失败，请检查配置");
            }
        });
    }

    @Override
    public List<SearchFileVO> searchES(SearchFileQueryDTO searchFileQueryDTO) {
        int pageNum = (int) searchFileQueryDTO.getPageNum() - 1;
        int pageSize = (int) (searchFileQueryDTO.getPageSize() == 0 ? 10 : searchFileQueryDTO.getPageSize());

        SearchResponse<FileSearchDTO> search = null;
        try {
            search = elasticsearchClient.search(s -> s
                    .index("file_search")
                    .query(_1 -> _1
                            .bool(_2 -> _2
                                    .must(_3 -> _3
                                            .bool(_4 -> _4
                                                    .should(_5 -> _5
                                                            .match(_6 -> _6
                                                                    .field("fileName")
                                                                    .query(searchFileQueryDTO.getFileName())))
                                                    .should(_5 -> _5
                                                            .wildcard(_6 -> _6
                                                                    .field("fileName")
                                                                    .wildcard("*" + searchFileQueryDTO.getFileName() + "*")))
                                            ))
                                    .must(_3 -> _3
                                            .term(_4 -> _4
                                                    .field("userId")
                                                    .value(StpUtil.getLoginIdAsLong())))
                            ))
                    .from(pageNum)
                    .size(pageSize)
                    .highlight(h -> h
                            .fields("fileName", f -> f.type("plain")
                                    .preTags("<span class='keyword'>").postTags("</span>"))
                            .encoder(HighlighterEncoder.Html)), FileSearchDTO.class);
        } catch (IOException e) {
            e.printStackTrace();
        }

        List<SearchFileVO> searchFileVOList = new ArrayList<>();
        if (search != null) {
            for (Hit<FileSearchDTO> hit : search.hits().hits()) {
                SearchFileVO searchFileVO = new SearchFileVO();
                BeanUtil.copyProperties(hit.source(), searchFileVO);
                searchFileVO.setHighLight(hit.highlight());
                searchFileVOList.add(searchFileVO);

                // 如果文件不存在，也从ES中删除
                if (!feignService.getUserFile(searchFileVO.getUserFileId()).getData()) {
                    this.deleteES(searchFileVO.getUserFileId());
                }
            }
        }
        return searchFileVOList;
    }
}
